# import PacketAnalyzer
# import PacketDigester

from scapy.all import *
from scapy.utils import PcapReader
from collections import Counter, namedtuple
import math
import zlib as zl
import binascii as b2a
import random
import statistics


class PcapFeatures(object):

    def __init__(self, file_path):
        '''

        :param file_path: Set file path with 'None' if file_path given (indicates that it's a filtered pcap)
                else. set the actual file_Path
        :param protoLabel: Used to label the base file where the pcap file-path will be stored
        :return:
        '''

        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger(__name__)
        #self.logger.setLevel(logging.INFO)
        self.logger.setLevel(logging.DEBUG)
        #self.logger.setLevel(logging.WARNING)

        self.pcapFilePath = str(file_path).strip()
        self.pcapFileName = ''
        try:
            if len(file_path) > 0:
                # self.pktReader = PcapReader(self.pcapFilePath)
                # Note the difference between the ABOVE and with '.read_all' BELOW
                self.pktReader = PcapReader(self.pcapFilePath).read_all()
                # CHECK THE LINE ABOVE IF MEMORY ISSUES ARISE FOR LARGE PCAP FILES
                self.pcapFileName = str(self.pcapFilePath).rsplit('/', 1)[1]
                #self.logger.debug("Pcap File Name: %s" % self.pcapFileName)
        except:
            self.logger.warning(
                "Pcap File MISSING at : [%s] or Filtered PCAP" %
                self.pcapFilePath)

        #self.protocolLabel = protoLabel

        self.pktCharFreqDict = {}
        self.pktCharEntropySeq = []
        self.specificPktLens = []

        #self.fig, self.ax = plt.subplots()
        #Originally plots were initialized here when the object was created
        #For memory reasons figures/plots/axes are initialized now only just before plotting
        self.fig = None  #plt.figure()
        self.ax = None  #plt.axes()

        #self.logger.info("Finished initializing and reading pcap file ...")
        #self.logger.debug("Type : %s" % str(type(self.pktReader)))

    def test_pkt_Reader(self):
        self.logger.debug("Type : %s" % str(type(self.pktReader)))
        #pktlen_seq = [len(pkt[IP]) for pkt in self.pktReader if UDP in pkt]
        pktlen_seq = [len(pkt[IP]) for pkt in self.pktReader if UDP in pkt]
        self.logger.debug("Length of Seq: %i" % len(pktlen_seq))

        return pktlen_seq

    #def add_proto_label(self, newProtoLabel):
    #self.protocolLabel = newProtoLabel

    def get_proto_label(self):
        return self.protocolLabel

    def calcEntropy(self, myFreqDict):
        '''
        Entropy calculation function
        H(x) = sum [p(x)*log(1/p)] for i occurrences of x
        Arguments: Takes a dictionary containing byte/char keys and their frequency as the value
        '''
        h = 0.0
        for aKey in myFreqDict:
            # Calculate probability of each even occurrence
            prob = myFreqDict[aKey] / sum(myFreqDict.values())
            # Entropy formula
            h += prob * math.log((1 / prob), 2)
        return h


# #---------------------------------####
# ###### HTTP-S related Methods     ####
# #---------------------------------####

    def getHttp_S_ReqBytesHex(self):
        '''
        Get the Bytes of only the HTTP-S Request characters in TCP packets
        that have a payload and have the destination port = 443
        Change them to hex and convert the byte array into a hex string (each pair of hex values represents a byte)
        :return:
        '''
        # self.pktCharSeq = [(pkt[IP][TCP][Raw].load).decode()  # <--- Will get HTTP Request strings in normal ascii text
        self.pktCharSeq = [
            b2a.hexlify(bytes(pkt[IP][TCP][Raw].load)).decode()[:128]
            for pkt in self.pktReader if TCP in pkt and Raw in pkt
        ]
        return self.pktCharSeq

    def getHttp_S_ReqEntropy(self):
        '''
        Get the Entropy of only the HTTP-S Request characters in TCP packets
        that have a payload and have the destination port = 443
        :return:
        '''
        self.pktCharEntropySeq = [
            self.calcEntropy(Counter(bytes(pkt[IP][TCP][Raw].load)[:64]))
            for pkt in self.pktReader if TCP in pkt and Raw in pkt
        ]
        '''
        for pkt in self.pktReader:
            if TCP in pkt and Raw in pkt:
                print(Counter(bytes(pkt[IP][TCP][Raw].load)[:64]))
                print(sum(
                    Counter(bytes(pkt[IP][TCP][Raw].load)[:64]).values()))
        '''
        return self.pktCharEntropySeq


def calcEntropy2(myFreqDict):
    '''
        Entropy calculation function
        H(x) = sum [p(x)*log(1/p)] for i occurrences of x
        Arguments: Takes a dictionary containing byte/char keys and their frequency as the value
        '''
    h = 0.0
    for aKey in myFreqDict:
        # Calculate probability of each even occurrence
        prob = myFreqDict[aKey] / sum(myFreqDict.values())
        # Entropy formula
        h += prob * math.log((1 / prob), 2)
        h * prob
    return h


def mtkl_pcap(n):
    temp = []
    sum = 0.0
    stddd = []
    for i in range(n):
        x = []
        for i in range(64):
            x.append(str(random.randint(0, 255)))
        temp = Counter(x)
        prob = calcEntropy2(temp)
        stddd.append(prob)
        #print(x)
        #print(temp)
        #print(prob)
        sum += prob
    return sum / n - 3 * statistics.stdev(
        stddd), sum / n + 3 * statistics.stdev(stddd)


if __name__ == '__main__':
    file_path = os.path.dirname(__file__) + '/'
    file_name = dir_name + "ss4.0.7.pcap"
    file_data = read_pcap(file_name)
    pcap_feat = PcapFeatures(file_path)
    a = pcap_feat.getHttp_S_ReqBytesHex()
    b = pcap_feat.getHttp_S_ReqEntropy()
    #print(b)
    n = 10000
    ly = mtkl_pcap(n)
    if b[0] < ly[0] or b[0] > ly[1]:
        print("NO")
    else:
        print("yes")